<html>

<head></head>
<script src="https://unpkg.com/@tensorflow/tfjs"></script>
<script lang="js">
    // 癌症诊断
    // 参考文档：https://god.yanxishe.com/TextTranslation/2622

    async function run() {
        const trainingUrl = 'http://127.0.0.1:8080/tensorflow-js-1/data/wdbc-test.csv';

        // Take a look at the 'wdbc-train.csv' file and specify the column
        // that should be treated as the label in the space below.
        // HINT: Remember that you are trying to build a classifier that 
        // can predict from the data whether the diagnosis is malignant or benign.
        //查看“wdbc train.csv”文件并指定列
        //应将其视为下方空间中的标签。
        //提示：请记住，您正在尝试构建一个分类器
        //可以根据数据预测诊断是恶性还是良性。
        const trainingData = tf.data.csv(trainingUrl, {
            columnConfigs: {
                diagnosis: {
                    isLabel: true
                }
            }
        });

        // Convert the training data into arrays in the space below.
        // Note: In this case, the labels are integers, not strings.
        // Therefore, there is no need to convert string labels into
        // a one-hot encoded array of label values like we did in the
        // Iris dataset example. 
        // 将训练数据转换为下面空间中的数组。
        // 注意：在这种情况下，标签是整数，而不是字符串。
        // 因此，无需将字符串标签转换为
        // 标签值的一个热编码数组，就像我们在
        // Iris数据集示例。
        const convertedTrainingData = trainingData.map(({ xs, ys }) => {
            return { xs: Object.values(xs), ys: Object.values(ys) };
        }).batch(10)


        const testingUrl = 'http://127.0.0.1:8080/tensorflow-js-1/data/wdbc-train.csv';

        // Take a look at the 'wdbc-test.csv' file and specify the column
        // that should be treated as the label in the space below..
        // HINT: Remember that you are trying to build a classifier that 
        // can predict from the data whether the diagnosis is malignant or benign.
        // 查看“wdbc test.csv”文件并指定列
        // 应将其视为下方空间中的标签。。
        // 提示：请记住，您正在尝试构建一个分类器
        // 可以根据数据预测诊断是恶性还是良性。
        const testingData = tf.data.csv(testingUrl, {
            columnConfigs: {
                diagnosis: {
                    isLabel: true
                }
            }
        });

        // Convert the testing data into arrays in the space below.
        // Note: In this case, the labels are integers, not strings.
        // Therefore, there is no need to convert string labels into
        // a one-hot encoded array of label values like we did in the
        // Iris dataset example. 
        // 将测试数据转换为下面空间中的数组。
        // 注意：在这种情况下，标签是整数，而不是字符串。
        // 因此，无需将字符串标签转换为
        // 标签值的一个热编码数组，就像我们在
        // Iris数据集示例。
        const convertedTestingData = testingData.map(({ xs, ys }) => {
            return { xs: Object.values(xs), ys: Object.values(ys) };
        }).batch(10)

        // Specify the number of features in the space below.
        // HINT: You can get the number of features from the number of columns
        // and the number of labels in the training data. 
        // 在下面的空间中指定要素的数量。
        // 提示：可以从列的数量中获取要素的数量
        // 以及训练数据中的标签的数量。
        const numOfFeatures = (await trainingData.columnNames()).length - 1;

        // In the space below create a neural network that predicts 1 if the diagnosis is malignant
        // and 0 if the diagnosis is benign. Your neural network should only use dense
        // layers and the output layer should only have a single output unit with a
        // sigmoid activation function. You are free to use as many hidden layers and
        // neurons as you like.  
        // HINT: Make sure your input layer has the correct input shape. We also suggest
        // using ReLu activation functions where applicable. For this dataset only a few
        // hidden layers should be enough to get a high accuracy. 
        // 在下面的空间中创建一个神经网络，
        // 如果诊断为恶性，则预测1，如果诊断是良性，则预测0。
        // 你的神经网络应该只使用密集层，
        // 输出层应该只有一个具有S形激活函数的输出单元。
        // 你可以随意使用任意多的隐藏层和神经元。

        // 提示：确保您的输入层具有正确的输入形状。
        // 我们还建议在适用的情况下使用ReLu激活功能。
        // 对于这个数据集，只有几个隐藏层就足以获得高精度。
        const model = tf.sequential();
        // YOUR CODE HERE
        model.add(tf.layers.dense({ inputShape: [numOfFeatures], activation: "sigmoid", units: 5 }))
        model.add(tf.layers.dense({ activation: "relu", units: 10 }))
        model.add(tf.layers.dense({ activation: "sigmoid", units: 1 }))


        // Compile the model using the binaryCrossentropy loss, 
        // the rmsprop optimizer, and accuracy for your metrics. 
        model.compile({
            loss: "binaryCrossentropy",
            optimizer: tf.train.rmsprop(0.01),
            metrics: ['accuracy']
        });


        await model.fitDataset(convertedTrainingData,
            {
                epochs: 50,
                validationData: convertedTestingData,
                callbacks: {
                    onEpochEnd: async (epoch, logs) => {
                        console.log("Epoch: " + epoch + " Loss: " + logs.loss + " Accuracy: " + logs.acc);
                    }
                }
            });
        // 下载训练模型
        // await model.save('downloads://my_model');
        // 输入一个测试数据测试模型
        const testValue = tf.tensor2d([-0.2017560352, 0.3290785951, -0.1308675428, -0.2714550596, 1.029197687, 0.8641183587, 0.7336389793, 0.8566968842, 1.120327751, 1.553584804, -0.04197565532, -0.5158820604, 0.1315408672, -0.13875636, -0.5595397256, -0.137973541, 0.09807079797, 0.2875119649, -0.4244614077, 0.1130514903, 0.03150414385, 0.6762888632, 0.185286211, -0.0628080803, 1.10353068, 0.8744426707, 1.219090897, 1.389329095, 1.082032838, 1.540296642], [1, 30]);
        const prediction = model.predict(testValue);
        // alert(prediction)
    }
    run();
</script>

<body>
</body>

</html>